
#include <game_state/level_select/level_select.hpp>
#include <window/window.hpp>
#include <nmn/create_string.hpp>
#include <game_state/running/game_running.hpp>
#include <game_state/menu/menu_game_state.hpp>

#include <Input/InputSingleton.h>
#include <input/actions/actions.hpp>

#include <boost/filesystem.hpp>

level_select_state::level_select_state( )
	:		m_background( new sprite("./resources/images/level_select/background.tga") )
		,	m_selected( 0 )
		,	m_select_sound( *g_cache.load_buffer( "./resources/audio/menu_select.wav" ) )
		,	m_cycle_sound( *g_cache.load_buffer( "./resources/audio/menu_cycle.wav" ) )
		,	m_switching(false)
{
	boost::filesystem::path levels_directory( "./resources/data/level/" );
	levels_directory = boost::filesystem::system_complete( levels_directory );

	std::for_each( boost::filesystem::directory_iterator( levels_directory ), boost::filesystem::directory_iterator( ), [this]( boost::filesystem::path const & path )
	{
		if ( boost::filesystem::is_regular_file( path ) )
		{
			std::string const extension = path.extension( );
			if ( extension == ".level" )
			{
				m_levels.push_back( std::make_pair( path.leaf( ), sf::String( nmn::create_string( "%1%: %2%", m_levels.size( ) + 1, path.leaf( ) ) ) ) );
				sf::String & text = m_levels.back( ).second;

				text.SetColor( sf::Color( 0, 0, 0, 255 ) );
				text.SetCenter( 0.0f, text.GetRect( ).GetHeight( ) * 0.5f );
				text.SetX( 125.0f );
			}
		}
	} );

	if ( m_levels.empty( ) )
	{
		throw std::runtime_error( "No level files found" );
	}

	g_user_input.clearActionAssignments( );
	g_user_input.assignActionToKey( actions::menu_up, sf::Key::Up );
	g_user_input.assignActionToKey( actions::menu_down, sf::Key::Down );
	g_user_input.assignActionToKey( actions::menu_select, sf::Key::Space );
	g_user_input.assignActionToKey( actions::menu_cancel, sf::Key::Escape );
}

level_select_state::~level_select_state( )
{
}

game_state_ptr level_select_state::step( float delta_time )
{
	if ( !m_switching )
	{
		if ( g_user_input.getActionInput( actions::menu_up ).status == keyPressed )
		{
			if ( m_selected > 0 )
			{
				m_cycle_sound.Play( );
				--m_selected;
			}
		}
		else if ( g_user_input.getActionInput( actions::menu_down ).status == keyPressed )
		{
			if ( m_selected < ( m_levels.size( ) - 1 ) )
			{
				m_cycle_sound.Play( );
				++m_selected;
			}
		}

		if ( g_user_input.getActionInput( actions::menu_select ).status == keyPressed )
		{
			m_switching = true;
			m_select_sound.Play( );
		}
		else if ( g_user_input.getActionInput( actions::menu_cancel ).status == keyPressed )
		{
			return game_state_ptr( new menu_game_state( ) );
		}
	}
	else if ( m_select_sound.GetStatus( ) == sf::Sound::Stopped )
	{
		return game_state_ptr( new game_running_state( "./resources/data/level/" + m_levels[m_selected].first ) );
	}

	return shared_from_this( );
}

void level_select_state::draw( )
{
	m_background->draw( );

	size_t const spaces_per_side = 5;

	size_t const first_name_index = std::max<int>( 0, m_selected - static_cast<int>( spaces_per_side ) );
	size_t const last_name_index = std::min<int>( m_levels.size( ), m_selected + static_cast<int>( spaces_per_side ) + 1 );

	float const target = g_window->GetHeight( ) * 0.5f;
	float const top = 150.0f;
	float const half_size = target - top;
	float const distance_between_names = ( half_size ) / static_cast<float>( spaces_per_side );
	float const offset = distance_between_names * static_cast<float>( std::max<int>( 0, static_cast<int>( spaces_per_side ) - m_selected ) );

	for ( size_t i = first_name_index; i < last_name_index; ++i )
	{
		float const y = top + offset + static_cast<float>( i - first_name_index ) * distance_between_names;
		float const alpha = 1.0f - ( std::abs( y - target ) / ( half_size + distance_between_names ) );

		m_levels[i].second.SetY( y );

		if ( i == m_selected )
		{
			m_levels[i].second.SetColor( sf::Color( 128, 0, 0, static_cast<sf::Uint8>( alpha * 255 ) ) );
		}
		else
		{
			m_levels[i].second.SetColor( sf::Color( 0, 0, 0, static_cast<sf::Uint8>( alpha * 255 ) ) );
		}

		g_window->Draw( m_levels[i].second );
	}
}
